package org.stone.project.webIntegration.filter;


import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.Getter;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.stone.project.common.pojo.constant.SysConstant;
import org.stone.project.common.pojo.constant.WebFilterOrder;
import org.stone.project.common.pojo.exception.ServiceException;
import org.stone.project.logInterface.utils.LogUtil;
import org.stone.project.webIntegration.pojo.prop.WebFilterConfigProp;
import org.stone.project.webIntegration.utils.HttpHelper;
import org.stone.project.webIntegration.utils.ServletUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;

/**
 * 可重复读接口
 */
@Component
@Order(WebFilterOrder.HTTP_BODY_WRAP_ORDER)
public class HttpBodyRepeatReadFilter implements Filter {

    private static final Logger log = LoggerFactory.getLogger(HttpBodyRepeatReadFilter.class);

    @Autowired
    private WebFilterConfigProp webFilterConfigProp;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
        httpRequest = new MyRequestWrapper(httpRequest,webFilterConfigProp.cacheMaxByteSize());
        httpResponse = new MyResponseWrapper(httpResponse,httpRequest.getRequestURI(),webFilterConfigProp.cacheMaxByteSize());

        filterChain.doFilter(httpRequest, httpResponse);

        if (((MyResponseWrapper)(httpResponse)).getCacheFlag()){
            ((MyResponseWrapper)(httpResponse)).realWriteOut();
        }
    }





    /**
     * 请求缓存代理
     */
    @Getter
    public static class MyRequestWrapper extends HttpServletRequestWrapper {
        /**
         * 缓存标识
         */
        private Boolean cacheFlag = false;

        /**
         * 数据结果
         */
        private byte[] body;

        /**
         * 构造方法
         * 请求流缓存  通过传递的ContentType触发
         */
        public MyRequestWrapper(HttpServletRequest request,Integer cacheMaxByteSize) throws IOException {
            super(request);
            request.setCharacterEncoding(SysConstant.UTF8);
            if (ServletUtils.probHasFileContentType(request.getContentType())){
                cacheFlag = true;
                body = HttpHelper.getBodyString(request).getBytes(SysConstant.UTF8);
                if (body.length > cacheMaxByteSize){
                    LogUtil.addSysErrLog("优化提醒：系统接口",getRequestURI(),"请求内容缓存过大");
                }
            }
        }


        @SneakyThrows
        @Override
        public ServletInputStream getInputStream()  {
            // 判断是否是二进制或可能含有二进制对象
            if (cacheFlag){
                final ByteArrayInputStream bais = new ByteArrayInputStream(body);
                return new ServletInputStream() {

                    @Override
                    public int read() throws IOException
                    {
                        return bais.read();
                    }

                    @Override
                    public int available() throws IOException
                    {
                        return body.length;
                    }

                    @Override
                    public boolean isFinished()
                    {
                        return false;
                    }

                    @Override
                    public boolean isReady()
                    {
                        return false;
                    }

                    @Override
                    public void setReadListener(ReadListener readListener)
                    {

                    }
                };
            }else{
                return super.getInputStream();
            }
        }


    }






    /**
     * 响应数据缓存
     */
    @Getter
    public static class MyResponseWrapper extends HttpServletResponseWrapper {
        /**
         * 请求地址
         */
        private String requestURI;

        /**
         * 缓存标识
         */
        private Boolean cacheFlag = true;

        /**
         * 最大缓存大小
         */
        private Integer cacheMaxByteSize;

        /**
         * 缓存的输出流
         */
        private ByteArrayOutputStream output = new ByteArrayOutputStream();

        /**
         * 缓存响应流中的数据结果
         * 只有执行过realWriteOut或getDataStream才会有数据
         */
        private byte[] body;

        public MyResponseWrapper(HttpServletResponse response,String requestURI,Integer cacheMaxByteSize) {
            super(response);
            setCharacterEncoding(SysConstant.UTF8);
            this.requestURI = requestURI;
            this.cacheMaxByteSize = cacheMaxByteSize;
        }

        /**
         * 响应流缓存  通过设置ContentType触发
         * @param type
         */
        @SneakyThrows
        @Override
        public void setContentType(String type) {
            cacheFlag = ServletUtils.probHasFileContentType(type);
            super.setContentType(type);
        }

        /**
         * 此方法尽管可以实现重复读取相应流，但是从此方法中读取的响应流还是只能读取一遍
         * 重复读取需要通过getDataStream方法重新读取
         * @return
         * @throws IOException
         */
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            if (cacheFlag){
                return new ServletOutputStream() {
                    @Override
                    public boolean isReady() {
                        return false;
                    }

                    @Override
                    public void setWriteListener(WriteListener listener) {
                    }

                    @Override
                    public void write(int b) throws IOException {
                        output.write(b);
                    }

                    @Override
                    public void write(byte[] b) throws IOException {
                        output.write(b);
                    }

                    @Override
                    public void write(byte[] b, int off, int len) throws IOException {
                        output.write(b,off,len);
                    }
                };
            }else {
                return super.getOutputStream();
            }
        }

        /**
         * 获取缓存的输出流
         * @return
         */
        @SneakyThrows
        public ByteArrayInputStream getDataStream() {
            if (cacheFlag){
                if (ObjectUtil.isNull(body)){
                    body = ((ByteArrayOutputStream)output).toByteArray();
                    output.close();
                    if (body.length > cacheMaxByteSize){
                        LogUtil.addSysErrLog("优化提醒：系统接口",getRequestURI(),"响应内容缓存过大");
                    }
                }
                return new ByteArrayInputStream(body);
            }else {
                //没有缓存不会调用此方法
                throw new ServiceException("接口为"+requestURI+"不支持响应内容缓存");
            }
        }

        //真实写出响应数据
        @SneakyThrows
        public void realWriteOut(){
            try (
                    ServletOutputStream sos = super.getOutputStream();
                    ByteArrayInputStream bis = getDataStream();
                    ){
                IoUtil.copy(bis,sos);
            }
        }
    }
}
